package org.atlantis.grosbeak.cable;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.atlantis.grosbeak.cable.action.ActionMethod;
import org.atlantis.grosbeak.cable.boult.ActionBoult;
import org.atlantis.grosbeak.cable.boult.ExceptionHandlerBoult;
import org.atlantis.grosbeak.cable.boult.InterceptorBoult;
import org.atlantis.grosbeak.cable.interceptor.Interceptor;
import org.atlantis.grosbeak.cable.interceptor.InterceptorChainImpl;
import org.atlantis.grosbeak.cable.interceptor.InterceptorOrder;
import org.atlantis.grosbeak.cable.template.JavaScriptTemplate;
import org.atlantis.grosbeak.cable.template.Template;
import org.atlantis.grosbeak.cable.template.TextTemplate;
import org.atlantis.grosbeak.depot.BoultDepot;
import org.atlantis.grosbeak.lang.Exceptions;
import org.atlantis.grosbeak.logger.Logger;
import org.atlantis.grosbeak.logger.LoggerFactory;

/**
 * 请求分发器。
 * 
 * @author <a href="mailto:seenoevil.cn@gmail.com">SeeNoEvil</a>
 * 
 */
class Postman {

	private static final Logger logger = LoggerFactory.getLogger(Postman.class);

	private ServletContext servletContext;
	private BoultDepot depot;
	private Interceptor[] interceptors = null;
	private ExceptionHandler exceptionHandler = null;
	private Map<String, Map<String, ActionDesc>> adMap = null;

	// 启动Postman分发器
	public void setUp(Provider provider) {
		logger.fatalFmt("Initialize Dispatcher ... ");
		this.servletContext = provider.getServletContext();
		this.depot = (BoultDepot) servletContext.getAttribute(BoultDepot.class
				.getName());
		if (depot == null)
			throw Exceptions.makeThrow(CableException.class,
					"Missing depot container.");

		adMap = new HashMap<String, Map<String, ActionDesc>>();

		Map<String, Object> actions = depot.callbacks(ActionBoult.class);
		String[] interceptorIds = depot.ids(InterceptorBoult.class);
		String[] exceptionHandlerIds = depot.ids(ExceptionHandlerBoult.class);
		String exceptionHandlerId = null;
		if (exceptionHandlerIds.length > 0)
			exceptionHandlerId = exceptionHandlerIds[0];

		initActions(actions);
		initInterceptor(interceptorIds);
		initExceptionHandler(exceptionHandlerId);
	}

	// 初始化“action”
	private void initActions(Map<String, Object> actions) {
		for (Map.Entry<String, Object> entry : actions.entrySet()) {
			String actionBeanId = entry.getKey();
			String actionBeanUrl = (String) entry.getValue();

			// 获取ActionBean对象和类型
			Class<?> actionBeanType = depot.getType(actionBeanId);
			Object actionBean = depot.get(actionBeanType, actionBeanId);

			// 获得该ActionBean中的所有ActionMethod
			List<Method> actionMethods = CableUtils
					.getActionMethods(actionBeanType);

			Map<String, ActionDesc> _map = new HashMap<String, ActionDesc>();

			for (Method actionMethod : actionMethods) {
				// 获取每个ActionMethod与之匹配的URL
				ActionMethod am = actionMethod
						.getAnnotation(ActionMethod.class);
				String actionMethodUrl = am.value();
				if (actionMethodUrl.equals(ActionMethod.DEFAULT)) {
					actionMethodUrl = actionMethod.getName();
				}
				_map.put(actionMethodUrl, new ActionDesc(actionBean,
						actionMethod));
			}
			adMap.put(actionBeanUrl, _map);
		}
	}

	// 初始化“拦截器”
	private void initInterceptor(String[] interceptorIds) {
		List<Interceptor> interceptorList = new ArrayList<Interceptor>();
		for (String interceptorId : interceptorIds) {
			interceptorList.add(depot.get(Interceptor.class, interceptorId));
		}
		interceptors = interceptorList.toArray(new Interceptor[0]);
		Arrays.sort(interceptors, new Comparator<Interceptor>() {
			public int compare(Interceptor i1, Interceptor i2) {
				InterceptorOrder io1 = i1.getClass().getAnnotation(
						InterceptorOrder.class);
				InterceptorOrder io2 = i2.getClass().getAnnotation(
						InterceptorOrder.class);
				int weight1 = (io1 == null) ? Integer.MAX_VALUE : io1.value();
				int weight2 = (io2 == null) ? Integer.MAX_VALUE : io2.value();
				if (weight1 == weight2)
					return i1.getClass().getName().compareTo(
							i2.getClass().getName());
				return (weight1 < weight2) ? (-1) : 1;
			}
		});
	}

	// 初始化“异常处理器”
	private void initExceptionHandler(String execeptionHandlerId) {
		if (execeptionHandlerId == null) {
			this.exceptionHandler = new DefaultExceptionHandler();
		} else {
			this.exceptionHandler = depot.get(ExceptionHandler.class,
					execeptionHandlerId);
		}
	}

	// TODO 对于文件上传的功能还没完成，想以后通过Interceptor来完成
	public boolean service(HttpServletRequest request,
			HttpServletResponse response) {
		RequestUrlKit urlKit = RequestUrlKit.instance(request);
		logger.debugFmt("Handle rquest for URL: '%s'", urlKit.getFullUrl());

		ActionDesc ad = null;

		String actionBeanUrl = urlKit.getActionBeanUrl();
		String actionMethodUrl = urlKit.getActionMethodUrl();
		Map<String, ActionDesc> actionBeanMap = adMap.get(actionBeanUrl);
		if (actionBeanMap != null) {
			ad = actionBeanMap.get(actionMethodUrl);
		}

		if (ad == null) {
			logger.infoFmt("Unable to find action bean for url '%s'", urlKit
					.getFullUrl());
			return false;
		}

		Class<?>[] types = ad.arguments;
		Object[] values = new Object[types.length];
		for (int i = 0; i < types.length; i++) {
			switch (types[i].getCanonicalName().hashCode()) {
			case -1231049311:
				values[i] = new DefaultRequestAware(request, urlKit.getParams());
				break;
			case 1074399738:
				values[i] = new DefaultSessionAware(request.getSession());
				break;
			case 1250931787:
				values[i] = new DefaultResponseAware(response);
				break;
			case -511929728:
				values[i] = new DefaultApplicationAware(request.getSession()
						.getServletContext());
				break;
			default:
				values[i] = null;
			}
		}

		Orderly orderly = new Orderly(ad, values);
		disposeOrderly(orderly, request, response);
		return true;
	}

	// 调用Action的方法
	private void disposeOrderly(Orderly orderly, HttpServletRequest request,
			HttpServletResponse response) {
		WebMetaStore.setSelf(servletContext, request, response);
		try {
			InterceptorChainImpl chains = new InterceptorChainImpl(interceptors);
			chains.doInterceptor(orderly);
			disposeResult(request, response, chains.getResult());
		} catch (Exception e) {
			disposeException(request, response, e);
		} finally {
			WebMetaStore.removeSelf();
		}
	}

	// 处理异常
	private void disposeException(HttpServletRequest request,
			HttpServletResponse response, Exception e) {
		exceptionHandler.handle(request, response, e);
	}

	// 处理返回结果
	private void disposeResult(HttpServletRequest request,
			HttpServletResponse response, Object result) throws Exception {
		if (result == null)
			return;
		if (result instanceof Template) {
			Template template = (Template) result;
			template.render(servletContext, request, response);
			return;
		} else if (result instanceof String) {
			String str = (String) result;
			if (str.startsWith("redirect:")) {
				response.sendRedirect(str.substring("redirect:".length()));
				return;
			} else if (str.startsWith("script:")) {
				String js = str.substring("script:".length());
				new JavaScriptTemplate(js).render(servletContext, request,
						response);
				return;
			} else {
				new TextTemplate(str).render(servletContext, request, response);
				return;
			}
		}
		throw Exceptions.makeThrow(CableException.class,
				"Unable to dispose result with type '%s'", result.getClass()
						.getName());
	}
}
